package com.css.commonredis.preheat;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 郭辉
 * @className RedisWarmUpUtil
 * @description 缓存预热工具类
 * @date 2022/11/5 9:27
 * @company 海康威视
 * @since 1.0.0
 */
@Slf4j
@AllArgsConstructor
@Component
public class RedisWarmUpUtil implements ApplicationListener<ApplicationReadyEvent> {

    private final RedisTemplate redisTemplate;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        redisWarmUp();
        System.out.println("ApplicationListener================ApplicationStartedEvent");
    }


    public void redisWarmUp() {
        log.debug(this.getClass().getName()+".redisWarmUp start{}");
        //1.加载驱动
        try {
            Class.forName(DBConfig.DB_DIVER);
            //      2.建立连接
            Connection con = DriverManager.getConnection(DBConfig.DB_URL, DBConfig.DB_USER_NAME, DBConfig.DB_USER_PASSWORD);
            //      3.预处理sql语句
            PreparedStatement pst = con.prepareStatement("select * from redis_warm_up ");
            //      4.执行sql语句
            ResultSet rs = pst.executeQuery();
            //      5.处理sql语句执行的结果
            while (rs.next()) {
                int id = rs.getInt("id");
                String cache_source = rs.getString("cache_source");
                String key = rs.getString("key");
                key = underlineToCamel(key, true);
                String type = rs.getString("type");
                log.debug(String.valueOf(id), cache_source, key, type);
                //执行数据源的sql
                PreparedStatement source = con.prepareStatement(cache_source);
                //获取结果
                ResultSet sourceResult = source.executeQuery();
                ResultSetMetaData metaData = sourceResult.getMetaData();
                if ("Map".equalsIgnoreCase(type)) {
                    while (sourceResult.next()) {
                        Map<String, Object> rowData = new HashMap<String, Object>();
                        for (int i = 1; i <= metaData.getColumnCount(); i++) {
                            rowData.put(metaData.getColumnName(i), sourceResult.getObject(i));
                        }
                        Map<String, Object> newMap = replaceHump(rowData);
                        toRedisHash(key, newMap);
                    }
                }
                if ("List".equalsIgnoreCase(type)) {
                    List<Map<String, Object>> list = new ArrayList<>(sourceResult.getRow());
                    while (sourceResult.next()) {
                        Map<String, Object> rowData = new HashMap<String, Object>();
                        for (int i = 1; i <= metaData.getColumnCount(); i++) {
                            rowData.put(metaData.getColumnName(i), sourceResult.getObject(i));
                        }
                        Map<String, Object> newMap = replaceHump(rowData);
                        list.add(newMap);
                    }
                    toRedisList(list);
                }
            }
            //      6.释放资源
            pst.close();
            con.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void toRedisHash(String key, Map<String, Object> businessDataEntity) throws NoSuchFieldException, IllegalAccessException {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(BusinessDataEntity.class));
        Random random = new Random();
        System.out.println(businessDataEntity.get(key).toString());
        redisTemplate.opsForHash().put("cache_redis_business_map_" + key + "_" + businessDataEntity.get(key).toString(),
                businessDataEntity.get(key).toString(),
                businessDataEntity);
        redisTemplate.expire("cache_redis_business_map_" + key + "_" + businessDataEntity.get(key).toString(),
                random.nextInt(100),
                TimeUnit.SECONDS);
    }

    private void toRedisList(Collection<Map<String, Object>> list) {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(list.getClass()));
        redisTemplate.opsForList().leftPushAll("cache_redis_business_hash_list", list);
        Random random = new Random();
        redisTemplate.expire("cache_redis_business_hash_list", random.nextInt(100), TimeUnit.SECONDS);
    }

    /**
     * @description: 反射调用set方法给字段赋值
     * @author: guohui13
     * @date: 2022/11/8 9:15
     * @param: sourceResult，businessDataEntity，field
     * @return: null
     **/
    private void reflexSetField(ResultSet sourceResult, BusinessDataEntity businessDataEntity, Field field) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, SQLException {
        Method declaredMethod = businessDataEntity.getClass().getDeclaredMethod("set" + field.getName().substring(0, 1).toUpperCase() + field.getName().substring(1), field.getType());
        if (field.getType() == Integer.class || field.getType() == int.class) {
            declaredMethod.invoke(businessDataEntity, sourceResult.getInt(field.getName()));
        }
        if (field.getType() == String.class) {
            declaredMethod.invoke(businessDataEntity, sourceResult.getString(field.getName()));
        }
    }

    public static Map<String, Object> replaceHump(Map<String, Object> oldMap) {
        Map<String, Object> newObjectMap = new HashMap<String, Object>();
        Set<Map.Entry<String, Object>> entries = oldMap.entrySet();
        Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> next = iterator.next();
            String key = next.getKey();
            Object value = oldMap.get(key);
            if (key.contains("_")) {
                key = underlineToCamel(key, true);
            }
            newObjectMap.put(key, value);
            iterator.remove();
        }
        return newObjectMap;
    }

    /**
     *   * 下划线转驼峰法
     *   * @param line 源字符串
     *   * @param smallCamel 大小驼峰,是否为小驼峰
     *   * @return 转换后的字符串
     *   
     */
    public static String underlineToCamel(String line, boolean smallCamel) {
        if (line == null || "".equals(line)) {
            return "";
        }
        StringBuffer sb = new StringBuffer();
        Pattern pattern = Pattern.compile("([A-Za-z\\d]+)(_)?");
        Matcher matcher = pattern.matcher(line);
        while (matcher.find()) {
            String word = matcher.group();
            sb.append(smallCamel && matcher.start() == 0 ? Character.toLowerCase(word.charAt(0)) : Character.toUpperCase(word.charAt(0)));
            int index = word.lastIndexOf('_');
            if (index > 0) {
                sb.append(word.substring(1, index).toLowerCase());
            } else {
                sb.append(word.substring(1).toLowerCase());
            }
        }
        return sb.toString();
    }


}
